import PySimpleGUI as sg
import psutil, platform

ALPHA = 0.7
GRAPH_WIDTH = 120
GRAPH_HEIGHT = 40
TRANSPARENCY = .8
NUM_COLS = 4
POLL_FREQUENCY = 1000
UPDATE_FREQUENCY_MILLISECONDS = 20 * 1000
THEME = 'black'
colors = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29')


class DashGraph(object):
    def __init__(self, graph_elem, text_elem, starting_count, color):
        self.graph_current_item = 0
        self.graph_elem = graph_elem  # type: sg.Graph
        self.text_elem = text_elem
        self.prev_value = starting_count
        self.max_sent = 1
        self.color = color
        self.line_list = []

    def graph_percentage_abs(self, value):
        self.line_list.append(self.graph_elem.draw_line(
            (self.graph_current_item, 0),
            (self.graph_current_item, value),
            color=self.color))
        # print(self.line_list)
        if self.graph_current_item >= GRAPH_WIDTH:
            self.graph_elem.move(-1, 0)
            self.graph_elem.delete_figure(self.line_list[0])
            self.line_list = self.line_list[1:]
        else:
            self.graph_current_item += 1

    def text_display(self, text):
        self.text_elem.update(text)


def sys_gui(win_main):
    win_main.Hide()
    # cpu
    frame_layout_cpu = [[sg.Text(f'逻辑cpu个数：{psutil.cpu_count()}', font=("宋体", 10))],
                        [sg.Text(f'物理cpu个数：{psutil.cpu_count(logical=False)}', font=("宋体", 10))],
                        [sg.Text(f'可使用CPU个数：{len(psutil.Process().cpu_affinity())}', font=("宋体", 10))],
                        [sg.Text(f'CPU使用率：{psutil.cpu_percent(0)}%', font=("宋体", 10), key='CPU_Usage')],
                        [sg.Button('详情', key='get_CpuUsage', font=("宋体", 12), button_color='#B0C4DE')]]
    # 内存
    frame_layout_mem = [
        [sg.Text("内存总量：{} GB".format(round(psutil.virtual_memory().total / 1024 / 1024 / 1024, 1)), font=("宋体", 10))],
        [sg.Text('内存空闲：{} GB'.format(round(psutil.virtual_memory().free / 1024 / 1024 / 1024, 1)), font=("宋体", 10),
                 key='Mem_free')],
        [sg.Text('内存使用：{} GB'.format(round(psutil.virtual_memory().used / 1024 / 1024 / 1024, 1)), font=("宋体", 10),
                 key='Mem_used')],
        [sg.Text(f'内存使用率：{psutil.virtual_memory().percent}%', font=("宋体", 10), key='Mem_Usage')]]
    # 磁盘
    disk_total, disk_used, disk_free, disk_percent = disk_info()  # 获取disk数据并初始化
    frame_layout_disk = [
        [sg.Text(f'磁盘总量：{disk_total} GB', font=("宋体", 10))],
        [sg.Text(f'磁盘空闲：{disk_free} GB', font=("宋体", 10), key='disk_free')],
        [sg.Text(f'磁盘使用量：{disk_used} GB', font=("宋体", 10), key='disk_used')],
        [sg.Text(f'磁盘使用率：{disk_percent} GB', font=("宋体", 10), key='disk_Usage')],
        [sg.Button('详情', key='get_DiskUsage', font=("宋体", 12), button_color='#B0C4DE')]
    ]

    layout_sys = [
        [sg.Frame(layout=frame_layout_cpu, title='CPU', relief=sg.RELIEF_SUNKEN, font=("宋体", 12)),
         sg.Frame(layout=frame_layout_mem, title='内存使用', relief=sg.RELIEF_SUNKEN, font=("宋体", 12))
         ],
        [sg.Frame(layout=frame_layout_disk, title='磁盘', relief=sg.RELIEF_SUNKEN, font=("宋体", 12)),
         ],
        [sg.Button('返回工具箱', key='back_sys')]
    ]

    win_sys = sg.Window(f'{platform.system()}-System Info', layout_sys, font=("宋体", 15), default_element_size=(50, 1))
    while True:
        ev_sys, vals_sys = win_sys.Read(timeout=POLL_FREQUENCY)
        if ev_sys is None or ev_sys == 'back_sys':
            win_sys.close()
            win_main.UnHide()
            break
        # cpu使用率
        cpu = psutil.cpu_percent(0)
        win_sys['CPU_Usage'].update('CPU使用率：{0:2.0f}%'.format(cpu))
        # 内存使用详情
        win_sys['Mem_Usage'].update(value=f'内存使用率：{psutil.swap_memory().percent}%')
        win_sys['Mem_free'].update(
            value='内存空闲：{} GB'.format(round(psutil.virtual_memory().free / 1024 / 1024 / 1024, 1)))
        win_sys['Mem_used'].update(
            value='内存使用：{} GB'.format(round(psutil.virtual_memory().used / 1024 / 1024 / 1024, 1)))
        # 磁盘使用详情
        win_sys['disk_Usage'].update(value=f'磁盘使用率：{disk_percent} GB')
        win_sys['disk_free'].update(value=f'磁盘空闲：{disk_free} GB')
        win_sys['disk_used'].update(value=f'磁盘使用量：{disk_used} GB')
        # 网络详情
        # win_sys['net_in'].update(value=f'Net in：{int(human_size(psutil.net_io_counters().bytes_recv))}')
        # win_sys['net_out'].update(value=f'Net out：{int(human_size(psutil.net_io_counters().bytes_recv))}')
        # win_sys['CPU_Usage'].update(value=f'内存使用率：{psutil.cpu_percent(interval=1)}%')
        if ev_sys == 'get_CpuUsage':
            main()
        if ev_sys == 'get_DiskUsage':
            disk_gui(win_sys)


def disk_gui(win_main):
    sg.theme(THEME)
    BAR_COLORS = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29')
    layout = [[sg.Text('详情：' + ' ' * 28, font='Any 16'), sg.Text('[表情]', enable_events=True, key='Exit Text')]]
    particians = psutil.disk_partitions()
    for count, part in enumerate(particians):
        mount = part[0]
        try:
            bar_color = sg.theme_progress_bar_color()
            this_color = BAR_COLORS[count % len(BAR_COLORS)]
            usage = psutil.disk_usage(mount)
            stats_info = f'{round(usage.used / 1024 / 1024 / 1024, 1)}GB / {int(usage.total / 1024 / 1024 / 1024)}GB = {round(usage.free / 1024 / 1024 / 1024, 1)}GB free'
            layout += [[sg.Text(mount, size=(5, 1), key=('-NAME-', mount)),
                        sg.ProgressBar(100, 'h', size=(10, 15), key=('-PROG-', mount),
                                       bar_color=(this_color, bar_color[1])),
                        sg.Text(f'{usage.percent}%', size=(6, 1), key=('-%-', mount)),
                        sg.Text(stats_info, size=(24, 1), key=('-STATS-', mount))]]
        except:
            pass
    window = sg.Window('Drive status', layout, keep_on_top=True, grab_anywhere=True, no_titlebar=True,
                       alpha_channel=ALPHA, use_default_focus=False, finalize=True)
    update_window(window)  # 初始化进度条组件颜色
    while True:
        event, values = window.read(timeout=UPDATE_FREQUENCY_MILLISECONDS)  # 20s一次刷新
        update_window(window)
        if event in (None, 'Exit Text'):
            window.close()
        win_main.UnHide()
        break


def update_window(window):  # 进度颜色渲染
    particians = psutil.disk_partitions()
    for count, part in enumerate(particians):
        mount = part[0]
        try:
            usage = psutil.disk_usage(mount)
            window[('-PROG-', mount)].update_bar(int(usage.percent))  # 进度颜色渲染
        except:
            pass


def main():
    num_cores = len(psutil.cpu_percent(percpu=True))
    sg.theme('Black')
    red_x = "R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
    layout = [[sg.Button(image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window'),
               sg.Text(' CPU Core Usage')]]
    for rows in range(num_cores // NUM_COLS + 1):
        layout += [[GraphColumn('CPU ' + str(rows * NUM_COLS + cols), '_CPU_' + str(rows * NUM_COLS + cols)) for cols in
                    range(min(num_cores - rows * NUM_COLS, NUM_COLS))]]
    window = sg.Window('CPU System Dashboard', layout,
                       keep_on_top=True,
                       auto_size_buttons=False,
                       grab_anywhere=True,
                       no_titlebar=True,
                       default_button_element_size=(12, 1),
                       return_keyboard_events=True,
                       alpha_channel=TRANSPARENCY,
                       use_default_focus=False,
                       element_padding=(0, 0),
                       border_depth=0,
                       margins=(1, 1),
                       finalize=True)
    graphs = [DashGraph(window['_CPU_' + str(i) + '_GRAPH_'],
                        window['_CPU_' + str(i) + '_TXT_'],
                        0, colors[i % 6]) for i in range(num_cores)]
    while True:
        event, values = window.read(timeout=POLL_FREQUENCY)
        if event in (None, 'Exit'):
            window.UnHide()
            break
        stats = psutil.cpu_percent(percpu=True)
        for i, util in enumerate(stats):
            graphs[i].graph_percentage_abs(util)
            graphs[i].text_display('{} CPU {:2.0f}%'.format(i, util))
    window.close()


def Txt(text, **kwargs):
    return (sg.Text(text, font=('Helvetica 8'), **kwargs))


def GraphColumn(name, key):
    return sg.Column([[Txt(name, size=(10, 1), key=key + '_TXT_'), ],
                      [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black',
                                key=key + '_GRAPH_')]], pad=(2, 2))


def disk_info():
    particians = psutil.disk_partitions()
    total = free = used = percent = 0
    for count, part in enumerate(particians):
        mount = part[0]
        try:
            usage = psutil.disk_usage(mount)
            total += int(usage.total / 1024 / 1024 / 1024)
            used += round((usage.used / 1024 / 1024 / 1024), 1)
            used = round(used, 1)
            free += round((usage.free / 1024 / 1024 / 1024), 1)
            free = round(free, 1)
            percent = f"{round((used / total) * 100, 1)}%"
        except:
            pass
    return total, used, free, percent


def human_size(bytes, units=(' bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
    return str(bytes) + units[0] if bytes < 1024 else human_size(bytes >> 10, units[1:])
